home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / osr5 / sco / scripts / admin / memuse < prev    next >
Encoding:
AWK Script  |  1997-08-26  |  31.8 KB  |  872 lines

  1. #!/usr/local/bin/gawk -f
  2. # @(#) memuse.gawk 2.0 96/04/23
  3. # 96/01/21 John H. DuBois III (john@armory.com)
  4. # 96/04/23 Added assorted extra information, and all options.
  5.  
  6. # swap -l output:
  7. #path               dev  swaplo blocks   free
  8. #/dev/swap          1,105     0 196608 119992
  9. BEGIN {
  10.     Name = "memuse"
  11.     Usage = "Usage: " Name " [-h] [-s<seconds>] [-r<num>]"
  12.     ARGC = Opts(Name,Usage,"hs>r>",0)
  13.     if ("h" in Options) {
  14.     printf \
  15. "%s: show memory usage statistics.\n"\
  16. "%s\n"\
  17. "Options:\n"\
  18. "-s<seconds>: Print values subject to change every <seconds> seconds.\n"\
  19. "    If -r is not also given, values are printed until %s is interrupted.\n"\
  20. "-r<num>: Print values subject to change <num> times.\n"\
  21. "    If -s is not also given, they are printed about once per second.\n",
  22. Name,Usage,Name
  23.     exit 0
  24.     }
  25.     if ("s" in Options)
  26.     Seconds = " -s" Options["s"]
  27.     if ("r" in Options && Options["r"] > 1)
  28.     Repeat = " -r" Options["r"]
  29.     print "Swap space:"
  30.     cmd = "exec swap -l"
  31.     format = "    %-9.9s %6s %5s %5s\n"
  32.     printf format,"Device","Total","Free","Used"
  33.     cmd | getline
  34.     while ((cmd | getline) == 1) {
  35.     numSwap++
  36.     if (NF != 5) {
  37.         print "Strange line from swap -l: " $0
  38.         continue
  39.     }
  40.     blocksT += $4
  41.     freeT += $5
  42.     printSwap($1,$4,$5)
  43.     }
  44.     close(cmd)
  45.     if (numSwap > 1)
  46.     printSwap("Total",blocksT,freeT)
  47.     CmdReadLine("exec /etc/memsize 2>/dev/null")
  48.     # If memsize works, we're probably root so can use crash.
  49.     Cmd = "exec kvar -VRSnt' '" Seconds Repeat
  50.     if ($1 > 0) {
  51.     # memsize reports in bytes; convert to KB.
  52.     memSize = $1/1024
  53.     printLine(memSize,"Real memory (RAM)")
  54.     vmem = blocksT/2 + memSize
  55.     printLine(vmem,"Total virtual memory (RAM + swap)")
  56.     while ((Cmd | getline) == 1) {
  57.         if (DoNewline)
  58.         print ""
  59.         else
  60.         DoNewline = 1
  61.         PrintVarStats($1,$2,$3)
  62.     }
  63.     }
  64. }
  65.  
  66. function PrintVarStats(availsmem,freemem,freeSwap,
  67. realUsed,allocated,inUse,allocUnused) {
  68.     printLine(availsmem,
  69.     "Available (unallocated) virtual memory (availsmem)")
  70.     printLine(freemem,"Available (unused) real memory (freemem)")
  71.     realUsed = memSize - freemem
  72.     printLine(realUsed,"Real memory in use (real memory - freemem)")
  73.     allocated = vmem - availsmem
  74.     printLine(allocated,"Allocated virtual memory (vmem - availsmem)")
  75.     inUse = blocksT/2-freeSwap + realUsed
  76.     printLine(inUse,"Virtual memory in use (real memory used + swap used)")
  77.     allocUnused = allocated - inUse
  78.     printLine(allocUnused,
  79.     "Allocated but unused vmem (allocated vmem - vmem in use)")
  80. }
  81.  
  82. function printLine(Val,S) {
  83.     # Print negative numbers as 0.  Negative numbers can result because
  84.     # discrepancies will arise since the various binaries used to gather
  85.     # information don't provide an instantaneous snapshot of the system,
  86.     # and also affect the numbers themselves.
  87.     printf "%7dK %4dM %s\n",max(Val,0),max((Val+512)/1024,0),S
  88. }
  89.  
  90. function printSwap(dev,blocks,free) {
  91.     printf format,dev,f(blocks),f(free),f(blocks-free)
  92. }
  93.  
  94. function f(i) {
  95.     return i2met(int(i/2),4,1,1) "B"
  96. }
  97.  
  98. # @(#) i2met.awk 1.0 96/01/14
  99. # jhdiii 96/01/14
  100. # Convert positive integer value Value to a string at most MaxLen characters
  101. # long.  This is done by converting the integer to a string of the form n*m,
  102. # where m is a metric suffix from: K M G
  103. # If Pow2 is true, then each factor of 1K is taken to be 1024; if it is false,
  104. # it is taken to be 1000.
  105. # MaxLen must be between 4 and 9. 
  106. # Value may be any integer from 0..maxint
  107. # If Units is given, it is the units that Value is passed in, where
  108. # Units=1 means Value is in K; Units=2 means Value is in M, etc.
  109. function i2met(Value,MaxLen,Pow2,Units,  Len,Div) {
  110.     if (!(1 in Suf))
  111.     split("K,M,G,T,P,E,Z,Y",Suf,",")
  112.     # In both awk & gawk, integer values that can be represented as
  113.     # machine integers will be printed as integers.
  114.     # If value can be printed without modification, return it as it,
  115.     # but with a multiplier suffix if reqd.
  116.     if ((Len = length(Value Suf[Units])) <= MaxLen)
  117.     return Value Suf[Units]
  118.     MaxLen -= 1        # Leave space for suffix
  119.     Div = Pow2 ? 1024 : 1000
  120.     for (Units += 1; Units in Suf; Units++)
  121.     if (length(int(Value /= Div)) <= MaxLen)
  122.         break
  123.     Value = substr(sprintf("%." MaxLen "f",Value),1,MaxLen)
  124.     if (substr(Value,MaxLen,1) == ".")
  125.     Value = substr(Value,1,MaxLen-1)
  126.     return Value Suf[Units]
  127. }
  128.  
  129. # @(#) CmdReadLine 95/09/04
  130. # Run Command, read a single line of output from it, then close it.
  131. # If Verbose is true, a complaint is issued if the read fails.
  132. # Output is returned in $*
  133. # The return value from getline is returned.  It will be 1 on a successful
  134. # read; 0 if no lines were read due because the command produced no output
  135. # or could not be run.  ERRNO is never set since pipes are run by a shell.
  136. function CmdReadLine(Command,Verbose,  ret) {
  137.     if (Debug) {
  138.     print "* Issuing command: " Command "\n"\
  139.           "* Waiting for single line of output..." > "/dev/stderr"
  140.     }
  141.     ret = Command | getline
  142.     if (Verbose && ret != 1)
  143.     printf "Read from pipe '%s' failed\n",Command
  144.     # close doesn't return a value under awk, only gawk
  145.     close(Command)
  146.     if (Debug)
  147.     print "* Output: " $0 > "/dev/stderr"
  148.     return ret
  149. }
  150.  
  151. ### Begin min,max,In routines
  152.  
  153. function min(a,b) {
  154.     if (a < b)
  155.     return a
  156.     else
  157.     return b
  158. }
  159.  
  160. function max(a,b) {
  161.     if (a > b)
  162.     return a
  163.     else
  164.     return b
  165. }
  166.  
  167. function In(Val,Min,Max) {
  168.     return (Min <= Val && Val <= Max)
  169. }
  170.  
  171. # Return (in Ind) the indices of the elements with the smallest value in A.
  172. # The smallest value is returned as the function value.
  173. # If there are no elements in A, null is returned.
  174. function arrMin(A,Ind,  i,min) {
  175.     for (i in A)
  176.     if (min == "" || A[i] < min) {
  177.         DeleteAll(Ind)
  178.         min = A[i]
  179.         Ind[i]
  180.     }
  181.     else if (A[i] == min)
  182.         Ind[i]
  183.     return min
  184. }
  185.  
  186. ### End min,max,In routines
  187.  
  188. ### Start of ProcArgs library
  189. # @(#) ProcArgs 1.11 96/12/08
  190. # 92/02/29 john h. dubois iii (john@armory.com)
  191. # 93/07/18 Added "#" arg type
  192. # 93/09/26 Do not count -h against MinArgs
  193. # 94/01/01 Stop scanning at first non-option arg.  Added ">" option type.
  194. #          Removed meaning of "+" or "-" by itself.
  195. # 94/03/08 Added & option and *()< option types.
  196. # 94/04/02 Added NoRCopt to Opts()
  197. # 94/06/11 Mark numeric variables as such.
  198. # 94/07/08 Opts(): Do not require any args if h option is given.
  199. # 95/01/22 Record options given more than once.  Record option num in argv.
  200. # 95/06/08 Added ExclusiveOptions().
  201. # 96/01/20 Let rcfiles be a colon-separated list of filenames.
  202. #          Expand $VARNAME at the start of its filenames.
  203. #          Let varname=0 and -option- turn off an option.
  204. # 96/05/05 Changed meaning of 7th arg to Opts; now can specify exactly how many
  205. #          of the vars should be searched for in the environment.
  206. #          Check for duplicate rcfiles.
  207. # 96/05/13 Return more specific error values.  Note: ProcArgs() and InitOpts()
  208. #          now return various negatives values on error, not just -1, and
  209. #          Opts() may set Err to various positive values, not just 1.
  210. #          Added AllowUnrecOpt.
  211. # 96/05/23 Check type given for & option
  212. # 96/06/15 Re-port to awk
  213. # 96/10/01 Moved file-reading code into ReadConfFile(), so that it can be
  214. #          used by other functions.
  215. # 96/10/15 Added OptChars
  216. # 96/11/01 Added exOpts arg to Opts()
  217. # 96/11/16 Added ; type
  218. # 96/12/08 Added Opt2Set() & Opt2Sets()
  219. # 96/12/27 Added CmdLineOpt()
  220.  
  221. # optlist is a string which contains all of the possible command line options.
  222. # A character followed by certain characters indicates that the option takes
  223. # an argument, with type as follows:
  224. # :    String argument
  225. # ;    Non-empty string argument
  226. # *    Floating point argument
  227. # (    Non-negative floating point argument
  228. # )    Positive floating point argument
  229. # #    Integer argument
  230. # <    Non-negative integer argument
  231. # >    Positive integer argument
  232. # The only difference the type of argument makes is in the runtime argument
  233. # error checking that is done.
  234.  
  235. # The & option is a special case used to get numeric options without the
  236. # user having to give an option character.  It is shorthand for [-+.0-9].
  237. # If & is included in optlist and an option string that begins with one of
  238. # these characters is seen, the value given to "&" will include the first
  239. # char of the option.  & must be followed by a type character other than ":"
  240. # or ";".
  241. # Note that if e.g. &> is given, an option of -.5 will produce an error.
  242.  
  243. # Strings in argv[] which begin with "-" or "+" are taken to be
  244. # strings of options, except that a string which consists solely of "-"
  245. # or "+" is taken to be a non-option string; like other non-option strings,
  246. # it stops the scanning of argv and is left in argv[].
  247. # An argument of "--" or "++" also stops the scanning of argv[] but is removed.
  248. # If an option takes an argument, the argument may either immediately
  249. # follow it or be given separately.
  250. # "-" and "+" options are treated the same.  "+" is allowed because most awks
  251. # take any -options to be arguments to themselves.  gawk 2.15 was enhanced to
  252. # stop scanning when it encounters an unrecognized option, though until 2.15.5
  253. # this feature had a flaw that caused problems in some cases.  See the OptChars
  254. # parameter to explicitly set the option-specifier characters.
  255.  
  256. # If an option that does not take an argument is given,
  257. # an index with its name is created in Options and its value is set to the
  258. # number of times it occurs in argv[].
  259.  
  260. # If an option that does take an argument is given, an index with its name is
  261. # created in Options and its value is set to the value of the argument given
  262. # for it, and Options[option-name,"count"] is (initially) set to the 1.
  263. # If an option that takes an argument is given more than once,
  264. # Options[option-name,"count"] is incremented, and the value is assigned to
  265. # the index (option-name,instance) where instance is 2 for the second occurance
  266. # of the option, etc.
  267. # In other words, the first time an option with a value is encountered, the
  268. # value is assigned to an index consisting only of its name; for any further
  269. # occurances of the option, the value index has an extra (count) dimension.
  270.  
  271. # The sequence number for each option found in argv[] is stored in
  272. # Options[option-name,"num",instance], where instance is 1 for the first
  273. # occurance of the option, etc.  The sequence number starts at 1 and is
  274. # incremented for each option, both those that have a value and those that
  275. # do not.  Options set from a config file have a value of 0 assigned to this.
  276.  
  277. # Options and their arguments are deleted from argv.
  278. # Note that this means that there may be gaps left in the indices of argv[].
  279. # If compress is nonzero, argv[] is packed by moving its elements so that
  280. # they have contiguous integer indices starting with 0.
  281. # Option processing will stop with the first unrecognized option, just as
  282. # though -- was given except that unlike -- the unrecognized option will not be
  283. # removed from ARGV[].  Normally, an error value is returned in this case.
  284. # If AllowUnrecOpt is true, it is not an error for an unrecognized option to
  285. # be found, so the number of remaining arguments is returned instead.
  286. # If OptChars is not a null string, it is the set of characters that indicate
  287. # that an argument is an option string if the string begins with one of the
  288. # characters.  A string consisting solely of two of the same option-indicator
  289. # characters stops the scanning of argv[].  The default is "-+".
  290. # argv[0] is not examined.
  291. # The number of arguments left in argc is returned.
  292. # If an error occurs, the global string OptErr is set to an error message
  293. # and a negative value is returned.
  294. # Current error values:
  295. # -1: option that required an argument did not get it.
  296. # -2: argument of incorrect type supplied for an option.
  297. # -3: unrecognized (invalid) option.
  298. function ProcArgs(argc,argv,OptList,Options,compress,AllowUnrecOpt,OptChars,
  299. ArgNum,ArgsLeft,Arg,ArgLen,ArgInd,Option,Pos,NumOpt,Value,HadValue,specGiven,
  300. NeedNextOpt,GotValue,OptionNum,Escape,dest,src,count,c,OptTerm,OptCharSet)
  301. {
  302. # ArgNum is the index of the argument being processed.
  303. # ArgsLeft is the number of arguments left in argv.
  304. # Arg is the argument being processed.
  305. # ArgLen is the length of the argument being processed.
  306. # ArgInd is the position of the character in Arg being processed.
  307. # Option is the character in Arg being processed.
  308. # Pos is the position in OptList of the option being processed.
  309. # NumOpt is true if a numeric option may be given.
  310.     ArgsLeft = argc
  311.     NumOpt = index(OptList,"&")
  312.     OptionNum = 0
  313.     if (OptChars == "")
  314.     OptChars = "-+"
  315.     while (OptChars != "") {
  316.     c = substr(OptChars,1,1)
  317.     OptChars = substr(OptChars,2)
  318.     OptCharSet[c]
  319.     OptTerm[c c]
  320.     }
  321.     for (ArgNum = 1; ArgNum < argc; ArgNum++) {
  322.     Arg = argv[ArgNum]
  323.     if (length(Arg) < 2 || !((specGiven = substr(Arg,1,1)) in OptCharSet))
  324.         break    # Not an option; quit
  325.     if (Arg in OptTerm) {
  326.         delete argv[ArgNum]
  327.         ArgsLeft--
  328.         break
  329.     }
  330.     ArgLen = length(Arg)
  331.     for (ArgInd = 2; ArgInd <= ArgLen; ArgInd++) {
  332.         Option = substr(Arg,ArgInd,1)
  333.         if (NumOpt && Option ~ /[-+.0-9]/) {
  334.         # If this option is a numeric option, make its flag be & and
  335.         # its option string flag position be the position of & in
  336.         # the option string.
  337.         Option = "&"
  338.         Pos = NumOpt
  339.         # Prefix Arg with a char so that ArgInd will point to the
  340.         # first char of the numeric option.
  341.         Arg = "&" Arg
  342.         ArgLen++
  343.         }
  344.         # Find position of flag in option string, to get its type (if any).
  345.         # Disallow & as literal flag.
  346.         else if (!(Pos = index(OptList,Option)) || Option == "&") {
  347.         if (AllowUnrecOpt) {
  348.             Escape = 1
  349.             break
  350.         }
  351.         else {
  352.             OptErr = "Invalid option: " specGiven Option
  353.             return -3
  354.         }
  355.         }
  356.  
  357.         # Find what the value of the option will be if it takes one.
  358.         # NeedNextOpt is true if the option specifier is the last char of
  359.         # this arg, which means that if the option requires a value it is
  360.         # the next arg.
  361.         if (NeedNextOpt = (ArgInd >= ArgLen)) { # Value is the next arg
  362.         if (GotValue = ArgNum + 1 < argc)
  363.             Value = argv[ArgNum+1]
  364.         }
  365.         else {    # Value is included with option
  366.         Value = substr(Arg,ArgInd + 1)
  367.         GotValue = 1
  368.         }
  369.  
  370.         if (HadValue = AssignVal(Option,Value,Options,
  371.         substr(OptList,Pos + 1,1),GotValue,"",++OptionNum,!NeedNextOpt,
  372.         specGiven)) {
  373.         if (HadValue < 0)    # error occured
  374.             return HadValue
  375.         if (HadValue == 2)
  376.             ArgInd++    # Account for the single-char value we used.
  377.         else {
  378.             if (NeedNextOpt) {    # option took next arg as value
  379.             delete argv[++ArgNum]
  380.             ArgsLeft--
  381.             }
  382.             break    # This option has been used up
  383.         }
  384.         }
  385.     }
  386.     if (Escape)
  387.         break
  388.     # Do not delete arg until after processing of it, so that if it is not
  389.     # recognized it can be left in ARGV[].
  390.     delete argv[ArgNum]
  391.     ArgsLeft--
  392.     }
  393.     if (compress != 0) {
  394.     dest = 1
  395.     src = argc - ArgsLeft + 1
  396.     for (count = ArgsLeft - 1; count; count--) {
  397.         ARGV[dest] = ARGV[src]
  398.         dest++
  399.         src++
  400.     }
  401.     }
  402.     return ArgsLeft
  403. }
  404.  
  405. # Assignment to values in Options[] occurs only in this function.
  406. # Option: Option specifier character.
  407. # Value: Value to be assigned to option, if it takes a value.
  408. # Options[]: Options array to return values in.
  409. # ArgType: Argument type specifier character.
  410. # GotValue: Whether any value is available to be assigned to this option.
  411. # Name: Name of option being processed.
  412. # OptionNum: Number of this option (starting with 1) if set in argv[],
  413. #     or 0 if it was given in a config file or in the environment.
  414. # SingleOpt: true if the value (if any) that is available for this option was
  415. #     given as part of the same command line arg as the option.  Used only for
  416. #     options from the command line.
  417. # specGiven is the option specifier character use, if any (e.g. - or +),
  418. # for use in error messages.
  419. # Global variables: OptErr
  420. # Return value: negative value on error, 0 if option did not require an
  421. # argument, 1 if it did & used the whole arg, 2 if it required just one char of
  422. # the arg.
  423. # Current error values:
  424. # -1: Option that required an argument did not get it.
  425. # -2: Value of incorrect type supplied for option.
  426. # -3: Bad type given for option &
  427. function AssignVal(Option,Value,Options,ArgType,GotValue,Name,OptionNum,
  428. SingleOpt,specGiven,  UsedValue,Err,NumTypes) {
  429.     # If option takes a value...    [
  430.     NumTypes = "*()#<>]"
  431.     if (Option == "&" && ArgType !~ "[" NumTypes) {    # ]
  432.     OptErr = "Bad type given for & option"
  433.     return -3
  434.     }
  435.  
  436.     if (UsedValue = (ArgType ~ "[:;" NumTypes)) {    # ]
  437.     if (!GotValue) {
  438.         if (Name != "")
  439.         OptErr = "Variable requires a value -- " Name
  440.         else
  441.         OptErr = "option requires an argument -- " Option
  442.         return -1
  443.     }
  444.     if ((Err = CheckType(ArgType,Value,Option,Name,specGiven)) != "") {
  445.         OptErr = Err
  446.         return -2
  447.     }
  448.     # Mark this as a numeric variable; will be propogated to Options[] val.
  449.     if (ArgType != ":" && ArgType != ";")
  450.         Value += 0
  451.     if ((Instance = ++Options[Option,"count"]) > 1)
  452.         Options[Option,Instance] = Value
  453.     else
  454.         Options[Option] = Value
  455.     }
  456.     # If this is an environ or rcfile assignment & it was given a value...
  457.     else if (!OptionNum && Value != "") {
  458.     UsedValue = 1
  459.     # If the value is "0" or "-" and this is the first instance of it,
  460.     # do not set Options[Option]; this allows an assignment in an rcfile to
  461.     # turn off an option (for the simple "Option in Options" test) in such
  462.     # a way that it cannot be turned on in a later file.
  463.     if (!(Option in Options) && (Value == "0" || Value == "-"))
  464.         Instance = 1
  465.     else
  466.         Instance = ++Options[Option]
  467.     # Save the value even though this is a flag
  468.     Options[Option,Instance] = Value
  469.     }
  470.     # If this is a command line flag and has a - following it in the same arg,
  471.     # it is being turned off.
  472.     else if (OptionNum && SingleOpt && substr(Value,1,1) == "-") {
  473.     UsedValue = 2
  474.     if (Option in Options)
  475.         Instance = ++Options[Option]
  476.     else
  477.         Instance = 1
  478.     Options[Option,Instance]
  479.     }
  480.     # If this is a flag assignment without a value, increment the count for the
  481.     # flag unless it was turned off.  The indicator for a flag being turned off
  482.     # is that the flag index has not been set in Options[] but it has an
  483.     # instance count.
  484.     else if (Option in Options || !((Option,1) in Options))
  485.     # Increment number of times this flag seen; will inc null value to 1
  486.     Instance = ++Options[Option]
  487.     Options[Option,"num",Instance] = OptionNum
  488.     return UsedValue
  489. }
  490.  
  491. # Option is the option letter
  492. # Value is the value being assigned
  493. # Name is the var name of the option, if any
  494. # ArgType is one of:
  495. # :    String argument
  496. # ;    Non-null string argument
  497. # *    Floating point argument
  498. # (    Non-negative floating point argument
  499. # )    Positive floating point argument
  500. # #    Integer argument
  501. # <    Non-negative integer argument
  502. # >    Positive integer argument
  503. # specGiven is the option specifier character use, if any (e.g. - or +),
  504. # for use in error messages.
  505. # Returns null on success, err string on error
  506. function CheckType(ArgType,Value,Option,Name,specGiven,  Err,ErrStr) {
  507.     if (ArgType == ":")
  508.     return ""
  509.     if (ArgType == ";") {
  510.     if (Value == "")
  511.         Err = "must be a non-empty string"
  512.     }
  513.     # A number begins with optional + or -, and is followed by a string of
  514.     # digits or a decimal with digits before it, after it, or both
  515.     else if (Value !~ /^[-+]?([0-9]+|[0-9]*\.[0-9]+|[0-9]+\.)$/)
  516.     Err = "must be a number"
  517.     else if (ArgType ~ "[#<>]" && Value ~ /\./)
  518.     Err = "may not include a fraction"
  519.     else if (ArgType ~ "[()<>]" && Value < 0)
  520.     Err = "may not be negative"
  521.     # (
  522.     else if (ArgType ~ "[)>]" && Value == 0)
  523.     Err = "must be a positive number"
  524.     if (Err != "") {
  525.     ErrStr = "Bad value \"" Value "\".  Value assigned to "
  526.     if (Name != "")
  527.         return ErrStr "variable " substr(Name,1,1) " " Err
  528.     else {
  529.         if (Option == "&")
  530.         Option = Value
  531.         return ErrStr "option " specGiven substr(Option,1,1) " " Err
  532.     }
  533.     }
  534.     else
  535.     return ""
  536. }
  537.  
  538. # Note: only the above functions are needed by ProcArgs.
  539. # The rest of these functions call ProcArgs() and also do other
  540. # option-processing stuff.
  541.  
  542. # Opts: Process command line arguments.
  543. # Opts processes command line arguments using ProcArgs()
  544. # and checks for errors.  If an error occurs, a message is printed
  545. # and the program is exited.
  546. #
  547. # Input variables:
  548. # Name is the name of the program, for error messages.
  549. # Usage is a usage message, for error messages.
  550. # OptList the option description string, as used by ProcArgs().
  551. # MinArgs is the minimum number of non-option arguments that this
  552. # program should have, non including ARGV[0] and +h.
  553. # If the program does not require any non-option arguments,
  554. # MinArgs should be omitted or given as 0.
  555. # rcFiles, if given, is a colon-seprated list of filenames to read for
  556. # variable initialization.  If a filename begins with ~/, the ~ is replaced
  557. # by the value of the environment variable HOME.  If a filename begins with
  558. # $, the part from the character after the $ up until (but not including)
  559. # the first character not in [a-zA-Z0-9_] will be searched for in the
  560. # environment; if found its value will be substituted, if not the filename will
  561. # be discarded.
  562. # rcfiles are read in the order given.
  563. # Values given in them will not override values given on the command line,
  564. # and values given in later files will not override those set in earlier
  565. # files, because AssignVal() will store each with a different instance index.
  566. # The first instance of each variable, either on the command line or in an
  567. # rcfile, will be stored with no instance index, and this is the value
  568. # normally used by programs that call this function.
  569. # VarNames is a comma-separated list of variable names to map to options,
  570. # in the same order as the options are given in OptList.
  571. # If EnvSearch is given and nonzero, the first EnvSearch variables will also be
  572. # searched for in the environment.  If set to -1, all values will be searched
  573. # for in the environment.  Values given in the environment will override
  574. # those given in the rcfiles but not those given on the command line.
  575. # NoRCopt, if given, is an additional letter option that if given on the
  576. # command line prevents the rcfiles from being read.
  577. # See ProcArgs() for a description of AllowUnRecOpt and optChars, and
  578. # ExclusiveOptions() for a description of exOpts.
  579. # Special options:
  580. # If x is made an option and is given, some debugging info is output.
  581. # h is assumed to be the help option.
  582.  
  583. # Global variables:
  584. # The command line arguments are taken from ARGV[].
  585. # The arguments that are option specifiers and values are removed from
  586. # ARGV[], leaving only ARGV[0] and the non-option arguments.
  587. # The number of elements in ARGV[] should be in ARGC.
  588. # After processing, ARGC is set to the number of elements left in ARGV[].
  589. # The option values are put in Options[].
  590. # On error, Err is set to a positive integer value so it can be checked for in
  591. # an END block.
  592. # Return value: The number of elements left in ARGV is returned.
  593. # Must keep OptErr global since it may be set by InitOpts().
  594. function Opts(Name,Usage,OptList,MinArgs,rcFiles,VarNames,EnvSearch,NoRCopt,
  595. AllowUnrecOpt,optChars,exOpts,  ArgsLeft,e) {
  596.     if (MinArgs == "")
  597.     MinArgs = 0
  598.     ArgsLeft = ProcArgs(ARGC,ARGV,OptList NoRCopt,Options,1,AllowUnrecOpt,
  599.     optChars)
  600.     if (ArgsLeft < (MinArgs+1) && !("h" in Options)) {
  601.     if (ArgsLeft >= 0) {
  602.         OptErr = "Not enough arguments"
  603.         Err = 4
  604.     }
  605.     else
  606.         Err = -ArgsLeft
  607.     printf "%s: %s.\nUse -h for help.\n%s\n",
  608.     Name,OptErr,Usage > "/dev/stderr"
  609.     exit 1
  610.     }
  611.     if (rcFiles != "" && (NoRCopt == "" || !(NoRCopt in Options)) &&
  612.     (e = InitOpts(rcFiles,Options,OptList,VarNames,EnvSearch)) < 0)
  613.     {
  614.     print Name ": " OptErr ".\nUse -h for help." > "/dev/stderr"
  615.     Err = -e
  616.     exit 1
  617.     }
  618.     if ((exOpts != "") && ((OptErr = ExclusiveOptions(exOpts,Options)) != ""))
  619.     {
  620.     printf "%s: Error: %s\n",Name,OptErr > "/dev/stderr"
  621.     Err = 1
  622.     exit 1
  623.     }
  624.     return ArgsLeft
  625. }
  626.  
  627. # ReadConfFile(): Read a file containing var/value assignments, in the form
  628. # <variable-name><assignment-char><value>.
  629. # Whitespace (spaces and tabs) around a variable (leading whitespace on the
  630. # line and whitespace between the variable name and the assignment character) 
  631. # is stripped.  Lines that do not contain an assignment operator or which
  632. # contain a null variable name are ignored, other than possibly being noted in
  633. # the return value.  If more than one assignment is made to a variable, the
  634. # first assignment is used.
  635. # Input variables:
  636. # File is the file to read.
  637. # Comment is the line-comment character.  If it is found as the first non-
  638. #     whitespace character on a line, the line is ignored.
  639. # Assign is the assignment string.  The first instance of Assign on a line
  640. #     separates the variable name from its value.
  641. # If StripWhite is true, whitespace around the value (whitespace between the
  642. #     assignment char and trailing whitespace on the line) is stripped.
  643. # VarPat is a pattern that variable names must match.  
  644. #     Example: "^[a-zA-Z][a-zA-Z0-9]+$"
  645. # If FlagsOK is true, variables are allowed to be "set" by being put alone on
  646. #     a line; no assignment operator is needed.  These variables are set in
  647. #     the output array with a null value.  Lines containing nothing but
  648. #     whitespace are still ignored.
  649. # Output variables:
  650. # Values[] contains the assignments, with the indexes being the variable names
  651. #     and the values being the assigned values.
  652. # Lines[] contains the line number that each variable occured on.  A flag set
  653. #     is record by giving it an index in Lines[] but not in Values[].
  654. # Return value:
  655. # If any errors occur, a string consisting of descriptions of the errors
  656. # separated by newlines is returned.  In no case will the string start with a
  657. # numeric value.  If no errors occur,  the number of lines read is returned.
  658. function ReadConfigFile(Values,Lines,File,Comment,Assign,StripWhite,VarPat,
  659. FlagsOK,
  660. Line,Status,Errs,AssignLen,LineNum,Var,Val) {
  661.     if (Comment != "")
  662.     Comment = "^" Comment
  663.     AssignLen = length(Assign)
  664.     if (VarPat == "")
  665.     VarPat = "."    # null varname not allowed
  666.     while ((Status = (getline Line < File)) == 1) {
  667.     LineNum++
  668.     sub("^[ \t]+","",Line)
  669.     if (Line == "")        # blank line
  670.         continue
  671.     if (Comment != "" && Line ~ Comment)
  672.         continue
  673.     if (Pos = index(Line,Assign)) {
  674.         Var = substr(Line,1,Pos-1)
  675.         Val = substr(Line,Pos+AssignLen)
  676.         if (StripWhite) {
  677.         sub("^[ \t]+","",Val)
  678.         sub("[ \t]+$","",Val)
  679.         }
  680.     }
  681.     else {
  682.         Var = Line    # If no value, var is entire line
  683.         Val = ""
  684.     }
  685.     if (!FlagsOK && Val == "") {
  686.         Errs = Errs \
  687.         sprintf("\nBad assignment on line %d of file %s: %s",
  688.         LineNum,File,Line)
  689.         continue
  690.     }
  691.     sub("[ \t]+$","",Var)
  692.     if (Var !~ VarPat) {
  693.         Errs = Errs sprintf("\nBad variable name on line %d of file %s: %s",
  694.         LineNum,File,Var)
  695.         continue
  696.     }
  697.     if (!(Var in Lines)) {
  698.         Lines[Var] = LineNum
  699.         if (Pos)
  700.         Values[Var] = Val
  701.     }
  702.     }
  703.     if (Status)
  704.     Errs = Errs "\nCould not read file " File
  705.     close(File)
  706.     return Errs == "" ? LineNum : substr(Errs,2)    # Skip first newline
  707. }
  708.  
  709. # Variables:
  710. # Data is stored in Options[].
  711. # rcFiles, OptList, VarNames, and EnvSearch are as as described for Opts().
  712. # Global vars:
  713. # Sets OptErr.  Uses ENVIRON[].
  714. # If anything is read from any of the rcfiles, sets READ_RCFILE to 1.
  715. function InitOpts(rcFiles,Options,OptList,VarNames,EnvSearch,
  716. Line,Var,Pos,Vars,Map,CharOpt,NumVars,TypesInd,Types,Type,Ret,i,rcFile,
  717. fNames,numrcFiles,filesRead,Err,Values,retStr) {
  718.     split("",filesRead,"")    # make awk know this is an array
  719.     NumVars = split(VarNames,Vars,",")
  720.     TypesInd = Ret = 0
  721.     if (EnvSearch == -1)
  722.     EnvSearch = NumVars
  723.     for (i = 1; i <= NumVars; i++) {
  724.     Var = Vars[i]
  725.     CharOpt = substr(OptList,++TypesInd,1)
  726.     if (CharOpt ~ "^[:;*()#<>&]$")
  727.         CharOpt = substr(OptList,++TypesInd,1)
  728.     Map[Var] = CharOpt
  729.     Types[Var] = Type = substr(OptList,TypesInd+1,1)
  730.     # Do not overwrite entries from environment
  731.     if (i <= EnvSearch && Var in ENVIRON &&
  732.     (Err = AssignVal(CharOpt,ENVIRON[Var],Options,Type,1,Var,0)) < 0)
  733.         return Err
  734.     }
  735.  
  736.     numrcFiles = split(rcFiles,fNames,":")
  737.     for (i = 1; i <= numrcFiles; i++) {
  738.     rcFile = fNames[i]
  739.     if (rcFile ~ "^~/")
  740.         rcFile = ENVIRON["HOME"] substr(rcFile,2)
  741.     else if (rcFile ~ /^\$/) {
  742.         rcFile = substr(rcFile,2)
  743.         match(rcFile,"^[a-zA-Z0-9_]*")
  744.         envvar = substr(rcFile,1,RLENGTH)
  745.         if (envvar in ENVIRON)
  746.         rcFile = ENVIRON[envvar] substr(rcFile,RLENGTH+1)
  747.         else
  748.         continue
  749.     }
  750.     if (rcFile in filesRead)
  751.         continue
  752.     # rcfiles are liable to be given more than once, e.g. UHOME and HOME
  753.     # may be the same
  754.     filesRead[rcFile]
  755.     if ("x" in Options)
  756.         printf "Reading configuration file %s\n",rcFile > "/dev/stderr"
  757.     retStr = ReadConfigFile(Values,Lines,rcFile,"#","=",0,"",1)
  758.     if (retStr > 0)
  759.         READ_RCFILE = 1
  760.     else if (ret != "") {
  761.         OptErr = retStr
  762.         Ret = -1
  763.     }
  764.     for (Var in Lines)
  765.         if (Var in Map) {
  766.         if ((Err = AssignVal(Map[Var],
  767.         Var in Values ? Values[Var] : "",Options,Types[Var],
  768.         Var in Values,Var,0)) < 0)
  769.             return Err
  770.         }
  771.         else {
  772.         OptErr = sprintf(\
  773.         "Unknown var \"%s\" assigned to on line %d\nof file %s",Var,
  774.         Lines[Var],rcFile)
  775.         Ret = -1
  776.         }
  777.     }
  778.  
  779.     if ("x" in Options)
  780.     for (Var in Map)
  781.         if (Map[Var] in Options)
  782.         printf "(%s) %s=%s\n",Map[Var],Var,Options[Map[Var]] > \
  783.         "/dev/stderr"
  784.         else
  785.         printf "(%s) %s not set\n",Map[Var],Var > "/dev/stderr"
  786.     return Ret
  787. }
  788.  
  789. # OptSets is a semicolon-separated list of sets of option sets.
  790. # Within a list of option sets, the option sets are separated by commas.  For
  791. # each set of sets, if any option in one of the sets is in Options[] AND any
  792. # option in one of the other sets is in Options[], an error string is returned.
  793. # If no conflicts are found, nothing is returned.
  794. # Example: if OptSets = "ab,def,g;i,j", an error will be returned due to
  795. # the exclusions presented by the first set of sets (ab,def,g) if:
  796. # (a or b is in Options[]) AND (d, e, or f is in Options[]) OR
  797. # (a or b is in Options[]) AND (g is in Options) OR
  798. # (d, e, or f is in Options[]) AND (g is in Options)
  799. # An error will be returned due to the exclusions presented by the second set
  800. # of sets (i,j) if: (i is in Options[]) AND (j is in Options[]).
  801. # todo: make options given on command line unset options given in config file
  802. # todo: that they conflict with.
  803. function ExclusiveOptions(OptSets,Options,
  804. Sets,SetSet,NumSets,Pos1,Pos2,Len,s1,s2,c1,c2,ErrStr,L1,L2,SetSets,NumSetSets,
  805. SetNum,OSetNum) {
  806.     NumSetSets = split(OptSets,SetSets,";")
  807.     # For each set of sets...
  808.     for (SetSet = 1; SetSet <= NumSetSets; SetSet++) {
  809.     # NumSets is the number of sets in this set of sets.
  810.     NumSets = split(SetSets[SetSet],Sets,",")
  811.     # For each set in a set of sets except the last...
  812.     for (SetNum = 1; SetNum < NumSets; SetNum++) {
  813.         s1 = Sets[SetNum]
  814.         L1 = length(s1)
  815.         for (Pos1 = 1; Pos1 <= L1; Pos1++)
  816.         # If any of the options in this set was given, check whether
  817.         # any of the options in the other sets was given.  Only check
  818.         # later sets since earlier sets will have already been checked
  819.         # against this set.
  820.         if ((c1 = substr(s1,Pos1,1)) in Options)
  821.             for (OSetNum = SetNum+1; OSetNum <= NumSets; OSetNum++) {
  822.             s2 = Sets[OSetNum]
  823.             L2 = length(s2)
  824.             for (Pos2 = 1; Pos2 <= L2; Pos2++)
  825.                 if ((c2 = substr(s2,Pos2,1)) in Options)
  826.                 ErrStr = ErrStr "\n"\
  827.                 sprintf("Cannot give both %s and %s options.",
  828.                 c1,c2)
  829.             }
  830.     }
  831.     }
  832.     if (ErrStr != "")
  833.     return substr(ErrStr,2)
  834.     return ""
  835. }
  836.  
  837. # The value of each instance of option Opt that occurs in Options[] is made an
  838. # index of Set[].
  839. # The return value is the number of instances of Opt in Options.
  840. function Opt2Set(Options,Opt,Set,  count) {
  841.     if (!(Opt in Options))
  842.     return 0
  843.     Set[Options[Opt]]
  844.     count = Options[Opt,"count"]
  845.     for (; count > 1; count--)
  846.     Set[Options[Opt,count]]
  847.     return count
  848. }
  849.  
  850. # The value of each instance of option Opt that occurs in Options[] that
  851. # begins with "!" is made an index of nSet[] (with the ! stripped from it).
  852. # Other values are made indexes of Set[].
  853. # The return value is the number of instances of Opt in Options.
  854. function Opt2Sets(Options,Opt,Set,nSet,  count,aSet,ret) {
  855.     ret = Opt2Set(Options,Opt,aSet)
  856.     for (value in aSet)
  857.     if (substr(value,1,1) == "!")
  858.         nSet[substr(value,2)]
  859.     else
  860.         Set[value]
  861.     return ret
  862. }
  863.  
  864. # Returns true if option Opt was given on the command line.
  865. function CmdLineOpt(Options,Opt,  i) {
  866.     for (i = 1; (Opt,"num",i) in Options; i++)
  867.     if (Options[Opt,"num",i] != 0)
  868.         return 1
  869.     return 0
  870. }
  871. ### End of ProcArgs library
  872.